package com.google.code.swingonfire.component.calendar;

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Insets;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;

import javax.swing.BorderFactory;
import javax.swing.JPanel;
import javax.swing.JTable;
import javax.swing.ListSelectionModel;
import javax.swing.table.JTableHeader;
import javax.swing.table.TableColumn;
import javax.swing.table.TableColumnModel;

import com.google.code.swingonfire.component.calendar.JMonthTableModel.DayOfWeekEntry;
import com.google.code.swingonfire.image.ComponentBasedImageProvider;

/**
 * A table for displaying one month
 * 
 * @author thred
 */
public class JMonthTable extends JPanel implements MouseListener, MouseMotionListener, ComponentListener
{

    private static final long serialVersionUID = -2475316069721157591L;

    private static final Insets BORDER = new Insets(6, 16, 12, 16);

    private final JCalendarModel model;
    private final int month;
    private final int year;
    private final boolean weekOfYearVisible;
    private final JTable table;
    private final ComponentBasedImageProvider backgroundImageProvider = new ComponentBasedImageProvider(this, false)
    {

        private final JCalendarBackgroundRenderer renderer = new DefaultJCalendarBackgroundRenderer();

        /**
         * {@inheritDoc}
         */
        @Override
        protected void drawImage(Graphics2D g, int width, int height)
        {
            int[] rows = new int[8];

            rows[0] = BORDER.top - 1;
            rows[1] = rows[0] + table.getTableHeader().getHeight();

            for (int i = 0; i < 6; i += 1)
            {
                rows[i + 2] = rows[i + 1] + table.getRowHeight();
            }

            int[] columns = new int[((weekOfYearVisible) ? 9 : 8)];

            columns[0] = BORDER.left - 1;

            for (int i = 0; i < ((weekOfYearVisible) ? 8 : 7); i += 1)
            {
                columns[i + 1] = columns[i] + table.getColumnModel().getColumn(i).getWidth();
            }

            renderer.drawMonthTableBackground(model, month, year, g, width, height, rows, columns, weekOfYearVisible);
        }
    };

    public JMonthTable(JCalendarModel model, int month, int year, boolean weekOfYearVisible)
    {
        super(new BorderLayout());

        this.model = model;
        this.month = month;
        this.year = year;
        this.weekOfYearVisible = weekOfYearVisible;

        setBorder(BorderFactory.createEmptyBorder(BORDER.top, BORDER.left, BORDER.bottom, BORDER.right));
        setOpaque(false);
        addComponentListener(this);

        table = new JTable(new JMonthTableModel(model, month, year, weekOfYearVisible));

        JTableHeader tableHeader = table.getTableHeader();

        tableHeader.setOpaque(false);
        tableHeader.setReorderingAllowed(false);

        add(tableHeader, BorderLayout.NORTH);
        add(table, BorderLayout.CENTER);

        JMonthTableDayRenderer dayRenderer = new JMonthTableDayRenderer();
        JMonthTableDayOfWeekRenderer dayOfWeekRenderer = new JMonthTableDayOfWeekRenderer();
        JMonthTableWeakOfYearRenderer weekOfYearRenderer = new JMonthTableWeakOfYearRenderer();
        TableColumnModel columnModel = table.getColumnModel();

        for (int i = 0; i < ((weekOfYearVisible) ? 8 : 7); i += 1)
        {
            TableColumn column = columnModel.getColumn(i);

            if (weekOfYearVisible)
            {
                if (i == 0)
                {
                    column.setCellRenderer(weekOfYearRenderer);
                    column.setHeaderValue(null);
                }
                else
                {
                    column.setCellRenderer(dayRenderer);
                    int dayOfWeek = ((((model.getFirstDayOfWeek() - 1) + i) - 1) % 7) + 1;
                    column.setHeaderValue(new DayOfWeekEntry(model, dayOfWeek));
                }
            }
            else
            {
                column.setCellRenderer(dayRenderer);
                int dayOfWeek = (((model.getFirstDayOfWeek() - 1) + i) % 7) + 1;
                column.setHeaderValue(new DayOfWeekEntry(model, dayOfWeek));
            }

            column.setHeaderRenderer(dayOfWeekRenderer);
            column.setResizable(false);
        }

        columnModel.setColumnSelectionAllowed(false);
        columnModel.getSelectionModel().setSelectionMode(ListSelectionModel.SINGLE_SELECTION);

        table.setShowGrid(false);
        table.setIntercellSpacing(new Dimension(1, 1));
        table.addMouseListener(this);
        table.addMouseMotionListener(this);
        table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
        table.setCellSelectionEnabled(true);
        table.setOpaque(false);

        resized();
    }

    public JCalendarModel getModel()
    {
        return model;
    }

    public int getMonth()
    {
        return month;
    }

    public int getYear()
    {
        return year;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void setSize(int width, int height)
    {
        super.setSize(width, height);

        resized();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void setSize(Dimension d)
    {
        super.setSize(d);

        resized();
    }

    private void resized()
    {
        int width = getWidth() - (BORDER.left + BORDER.right);
        int height = (getHeight() - (BORDER.top + BORDER.bottom)) / 7;

        if ((width > 0) && (height > 0))
        {
            table.getTableHeader().setPreferredSize(new Dimension(width, height));
            table.setRowHeight(height);
        }
    }

    @Override
    public void paintComponent(Graphics g)
    {
        g.drawImage(backgroundImageProvider.getImage(), 0, 0, null);

        super.paintComponent(g);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void mouseClicked(MouseEvent e)
    {
        // intentionally left blank
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void mousePressed(MouseEvent e)
    {
        // intentionally left blank
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void mouseReleased(MouseEvent e)
    {
        // intentionally left blank
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void mouseEntered(MouseEvent e)
    {
        // intentionally left blank
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void mouseExited(MouseEvent e)
    {
        table.clearSelection();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void mouseDragged(MouseEvent e)
    {
        // intentionally left blank
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void mouseMoved(MouseEvent e)
    {
        int column = table.columnAtPoint(e.getPoint());
        int row = table.rowAtPoint(e.getPoint());

        if ((column >= 0) && (row >= 0))
        {
            table.getSelectionModel().setSelectionInterval(row, row);
            table.getColumnModel().getSelectionModel().setSelectionInterval(column, column);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void componentResized(ComponentEvent e)
    {
        resized();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void componentMoved(ComponentEvent e)
    {
        // intentionally left blank
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void componentShown(ComponentEvent e)
    {
        // intentionally left blank
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void componentHidden(ComponentEvent e)
    {
        // intentionally left blank
    }

}
